package 一九年十二月份;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/*
* 给定一个包含 n 个整数的数组 nums 和一个目标值 target，判断 nums 中是否存在四个元素 a，b，c 和 d
*  ，使得 a + b + c + d 的值与 target 相等？找出所有满足条件且不重复的四元组。
注意：
答案中不可以包含重复的四元组。
示例：
给定数组 nums = [1, 0, -1, 0, -2, 2]，和 target = 0。
满足要求的四元组集合为：
[
  [-1,  0, 0, 1],
  [-2, -1, 1, 2],
  [-2,  0, 0, 2]
]
基本思路：
先后固定其中两个数值 i、j，再使用双指针寻找与目标合适的差值
* */
public class L_18 {
    public List<List<Integer>> fourSum(int[] nums, int target) {
        //定义一个返回值
        List<List<Integer>> result=new ArrayList<>();
        if(nums==null || nums.length<4){
            return result;
        }
        //对数组进行排序,升序
        Arrays.sort(nums);
        int length=nums.length;
        ///*定义4个指针k，i，j，h  k从0开始遍历，i从k+1开始遍历，留下j和h，j指向i+1，h指向数组最大值*/
        for (int k=0;k<length-3;k++){
            //当k的值与前面的值相等时忽略
            if(k>0 && nums[k]==nums[k-1]){
                continue;
            }
            //获取当前最小值，如果最小值比目标大，则break
            int mini=nums[k]+nums[k+1]+nums[k+2]+nums[k+3];
            if(mini>target){
                break;
            }
            /*获取当前最大值，如果最大值比目标值小，说明后面越来越小的值根本没戏，忽略*/
            int max1=nums[k]+nums[length-1]+nums[length-2]+nums[length-3];
            if(max1<target){
                continue;
            }
            //第二层循环，初始指针i指向k+1
            for (int i=k+1;i<length-2;i++){
                ///*当i的值与前面的值相等时忽略*
                if(i>k+1 && nums[i]==nums[i-1]){
                    continue;
                }
                /*定义指针j指向i+1*/
                int j=i+1;
                /*定义指针h指向数组末尾*/
                int h=length-1;

                //获取当前最小值，如果最小值比目标大，则忽略
                int min=nums[k]+nums[i]+nums[j]+nums[j+1];
                if(min>target){
                    continue;
                }
                /*获取当前最大值，如果最大值比目标值小，说明后面越来越小的值根本没戏，忽略*/
                int max=nums[k]+nums[i]+nums[h]+nums[h-1];
                if(max<target){
                    continue;
                }
                /*开始j指针和h指针的表演，计算当前和，如果等于目标值，j++并去重，h--并去重，
                当当前和大于目标值时h--，当当前和小于目标值时j++*/
                while (j<h){
                    int curr=nums[k]+nums[i]+nums[j]+nums[h];
                    if(curr==target){
                        result.add(Arrays.asList(nums[k],nums[i],nums[j],nums[h]));
                        j++;
                        while (j<h && nums[j]==nums[j-1]){  //去重
                            j++;
                        }
                        h--;
                        while (j<h && nums[h]==nums[h+1]){  //同理，去重
                            h--;
                        }
                    }else if(curr>target){
                        h--;
                    }else {
                     j++;
                    }
                }
            }
        }
        return result;
    }
}
